/////////////////////////////////////////////////////////////////////
//
//  MOE Framework (Meta Object Encapsulation Framework)
//
//  Original Author: Wiksy Wang, 2011
//
/////////////////////////////////////////////////////////////////////

#ifndef MOE_CLASS_META_H
#define MOE_CLASS_META_H

#include <boost/function.hpp>
#include <boost/noncopyable.hpp>
#include <boost/unordered_map.hpp>

#include "CommonDef.h"
#include "FunctionTypeDef.h"
#include "DataMemberMeta.h"
#include "RawByteVector.h"
#include "Assign/AssignProxy.hpp"

namespace MOE
{

class ClassMeta : boost::noncopyable
{
public:
	const ClassIDType GetClassID() const { return _metaClassID; }
	const RawByteVector& GetClassName() const { return _metaClassName; }
	const size_t GetSize() const { return _classSize; }
	const AtomTypes GetAtomType() const { return _atomType; }

	bool IsAtomClass() const;

	//_______________________________________________
	//
	//			DATA MEMBER MANIPULATION

	// Set object->field to be the value of *data
	bool Set(Address object, const RawByteVector& field, ConstAddress data) const;

	// Get the value of object->field and store it in *data
	bool Get(Address object, const RawByteVector& field, Address data) const;

	// Retrieve the address of object->field and output the address in *pDataAddr
	bool Access(Address object, const RawByteVector& field, Address* pDataAddr) const;

	// Templated convenient function for direct accessing different data types
	template <class DataType>
	bool Access(Address object, const RawByteVector& field, DataType** pDataAddr) const
	{
		Address addr;
		bool success = Access(object, field, &addr);
		*pDataAddr = static_cast<DataType*>(addr);
		return success;
	}

	const DataMemberMeta& Member(const RawByteVector& field) const;

	template <class VisitorType>
	void Visit(VisitorType& visitor) const
	{
		for (DataMemberMap::const_iterator it = _dataMembers.begin(), itEnd = _dataMembers.end(); it != itEnd; ++it)
		{
			visitor(it->first, it->second);
		}
	}

	ClassMeta& operator<<(const Assign::AssignProxy& proxy);

	//_______________________________________________
	//
	//			FUNCTION MEMBER MANIPULATION

	// TODO:
	// For now, we only have two function members: default constructor and destructor
	ClassMeta& DefineConstructor(const DefaultConstructorFuncType& constructor);
	ClassMeta& DefineDestructor(const DestructorFuncType& destructor);

	// Create a new object using the default constructor.
	Address New() const;

	// Delete / release memory of an object.
	void Delete(Address object) const;

private:
	ClassMeta(const ClassIDType classID, const RawByteVector& className, const size_t classSize, const AtomTypes atomType);

	// Class metadata
	ClassIDType _metaClassID;
	RawByteVector _metaClassName;
	size_t _classSize;
	AtomTypes _atomType;

	// Data members
	typedef boost::unordered_map<RawByteVector, DataMemberMeta> DataMemberMap;
	DataMemberMap _dataMembers;

	// Functions
	DefaultConstructorFuncType _defaultConstructor;
	DestructorFuncType _destructor;

	friend class ObjectTypeManager;
};

}

#endif
